A data storage system provides a pool of persistent data storage spaces for different clients. In the context of small computer system interface (SCSI), a client that sends input/output (I/O) requests is referred to as an initiator, and a data storage device that stores data is referred to as a target. Initiator and target are applicable not only to the traditional parallel SCSI, but also to Fibre Channel Protocol (FCP), Internet SCSI (iSCSI), HyperSCSI, Advanced Technology Attachment (ATA), Serial ATA (SATA), ATA over Ethernet (AoE), InfiniBand, and any other storage networking protocols.
A target can serve as many I/O requests as possible to one or more initiators. A set of software modules of the data storage system that processes I/O flows from an initiator to a target is referred to as an I/O stack. An I/O stack can include two main components: a data path that provides I/O processing to and from data objects; and a control path that manages the data objects required for the I/O processing in the data path. Examples of the control path management include, but are not limited to, addition, deletion, and modification of data objects.
In an I/O stack of a data storage system, control operations in the control path can modify the state or content of data objects that are used by the data path. This may cause contention and introduce synchronization primitives that can degrade the I/O performance and affect scalability of the data storage system. Typically, synchronization primitives between a control path and a data path are used to ensure that the data objects that are being used in the data path are not modified by the control path. The use of synchronization primitives requires that a data path process that executes I/O workloads has to acquire synchronization primitives, thus adding an overhead to the I/O processing and increasing the likelihood of contention. The synchronization primitives are required on the data objects themselves and also on a global list of data objects. As the scope of the synchronization primitives increases, the likelihood of contention among different data path threads running in the data path increases as well.
Interactions between a control path and a data path include an addition, a deletion, and modification of an object in the data storage. The data path is typically faster than the control path. The control path typically interacts with the same data objects that the data path has to access. Therefore, the control path and the data path may contend for the same data objects. The accesses of the data objects by the control path and the data path are typically protected by a synchronization mechanism. However, the synchronization mechanism may add a delay, including contention and longer code paths, resulting in degraded performance and parallelism.
In a conventional storage target implementation, data objects such as logical unit numbers (LUNs), connections, and access control lists (ACLs) are managed by a control path. The data object management by the control path includes addition of new objects, deletion or modification of existing data objects. These data objects are typically maintained using data structures such as lists, trees, or arrays. Although these storage management objects are owned and managed by the control path, a data path still has to reference these objects. In such a situation, a control path and a data path have to synchronize with each other by acquiring necessary locks.